/* Copyright Paul Reiche, Fred Ford. 1992-2002, Christopher Night 2013

This file is part of the HTML5 port of Ur-Quan Masters (UQM-HTML5)

UQM-HTML5 is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

UQM-HTML5 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with this program.  If not,
see <http://www.gnu.org/licenses/>. */


// Pre- and post-processing steps for battle/hyperspace
// Taken from src/uqm/process.c


// TODO: find this thing's home
var MAX_ZOOM_OUT = 4
var ARENA_WIDTH = SPACE_WIDTH * 2 * MAX_ZOOM_OUT
var ARENA_HEIGHT = SPACE_WIDTH * 2 * MAX_ZOOM_OUT
function WRAP_DELTA_X(dx) {
	return zmod(dx, SPACE_WIDTH)
}
function WRAP_DELTA_Y(dy) {
	return zmod(dy, SPACE_HEIGHT)
}

// TODO: start elements.h
var elements = []



var process = {
	camera: {
		x0: 0,
		y0: 0,
		zoom: 1,
	},

	UpdateQueue: function () {
		this.PreProcessQueue()
		this.ProcessCollisions()
		this.CalcView()
		this.PostProcessQueue()
	},

	RedrawQueue: function () {
		// Draw
		context.space.clear()
		galaxy.draw()
		for (var j = 0 ; j < elements.length ; ++j) {
			var element = elements[j], state = element.state
			if (state.DISAPPEARING || state.SKIP_DRAW) continue
			// TODO: break out drawing into member functions or event components
			var c = element.current
			var p = this.worldtoscreen(c.x, c.y)
			//if (linedrawn) console.log("overdrawing", element)
			if (element.origin) {
				var o = this.worldtoscreen(element.origin.x, element.origin.y)
				//console.log("laser draw on", battleFrameCount, elements[j].color, p, o, elements[j].origin, elements[j].current, elements[j].next, elements[j].state)
				context.space.drawline(element.color, o[0], o[1], p[0], p[1])
				var linedrawn = true
			} else if (c.frame.img) {
				// TODO: load/draw mini frames when zoomed out
				context.space.drawframe(c.frame, p[0], p[1], this.camera.zoom)
			} else {
				context.space.drawpoint(c.frame, p[0], p[1])
			}
		}
		context.flip()
		//if (linedrawn) throw "line drawn"
	},

	SetUpElement: function (element) {
		element.next = element.next || {}
//		if (element.origin) return // TODO: this is a temporary hack for lasers
		element.next.x = element.current.x
		element.next.y = element.current.y
		element.current.px = element.next.px = Math.round(element.current.x)
		element.current.py = element.next.py = Math.round(element.current.y)
		element.next.frame = element.current.frame
		element.state.APPEARING = false
		// if (collidingelement) ....
	},
	
	StepElement: function (element) {
		if (element.state.APPEARING) this.SetUpElement(element)
		element.current.x = element.next.x = rmod(element.next.x, ARENA_WIDTH)
		element.current.y = element.next.y = rmod(element.next.y, ARENA_HEIGHT)
		element.current.px = element.next.px = Math.round(element.current.x)
		element.current.py = element.next.py = Math.round(element.current.y)
		element.current.frame = element.next.frame
	},
	
	// Determine the appropriate zoom out level (called reduction) given the separations of the
	// enemy ships
	CalcReduction: function (dx, dy) {
		var xreduction = MAX_ZOOM_OUT * Math.abs(dx) / (0.25 * ARENA_WIDTH)
		var yreduction = MAX_ZOOM_OUT * Math.abs(dy) / (0.25 * ARENA_HEIGHT)
		var rmin = 1 // TODO: 2 for last battle
		var next_reduction = clip(Math.max(xreduction, yreduction), rmin, MAX_ZOOM_OUT)
		if (false) {  // TODO: opt.MeleeScaleStep or whatever this is
			next_reduction = next_reduction == 4 ? 4 : next_reduction >= 2 ? 2 : 1
		}
		return next_reduction
	},
	
	// I think it'll be a lot easier to just rewrite this rather than try to replicate the logic.
	CalcView: function () {
		var p0 = race_q[0].activeship.next, p1 = race_q[1].activeship.next
		var dx = zmod(p1.x - p0.x, ARENA_WIDTH), x0 = p0.x + 0.5 * dx
		var dy = zmod(p1.y - p0.y, ARENA_HEIGHT), y0 = p0.y + 0.5 * dy
		this.camera.x0 += zmod(x0 - this.camera.x0, ARENA_WIDTH)
		this.camera.y0 += zmod(y0 - this.camera.y0, ARENA_HEIGHT)
		this.camera.zoom = 1 / this.CalcReduction(dx, dy)
	},
	worldtoscreen: function (x, y) {
		// Wrap around for battle mode
		return [
			Math.round(0.5 * SPACE_WIDTH + zmod(x - this.camera.x0, ARENA_WIDTH) * this.camera.zoom),
			Math.round(0.5 * SPACE_HEIGHT - zmod(y - this.camera.y0, ARENA_HEIGHT) * this.camera.zoom),
		]
	},
	// Background stars scroll more slowly (by a factor of f)
	startoscreen: function (x, y, f) {
		return [
			Math.round(0.5 * SPACE_WIDTH + zmod((x - this.camera.x0) / f, ARENA_WIDTH) * this.camera.zoom),
			Math.round(0.5 * SPACE_HEIGHT - zmod((y - this.camera.y0) / f, ARENA_HEIGHT) * this.camera.zoom),
		]
	},

	PreProcess: function (element) {
		if (element.life_span == 0) {
			if (element.parent) this.Untarget(element)
			element.state.DISAPPEARING = true
			if (element.death_func) element.death_func()
		}
		var state = element.state
		if (!state.DISAPPEARING) {
			if (state.APPEARING) {
				this.SetUpElement(element)
			}
			if (element.preprocess_func && !state.APPEARING) {
				element.preprocess_func()
			}
			if (!state.IGNORE_VELOCITY) {
				// TODO: understand src/uqm/velocity.c:GetNextVelocityComponents
				if (element.vx || element.vy) {
					element.next.x += element.vx
					element.next.y += element.vy
				}
			}
			//if (state.CHANGING && !state.SKIP_COLLISION) InitIntersectEndPoint (element)
			if (state.FINITE_LIFE) --element.life_span
		}
		state.POST_PROCESS = false
		state.COLLISION = false
		state.PRE_PROCESS = true
	},
	
	PreProcessQueue: function () {
		for (var j = 0 ; j < elements.length ; ++j) {
			var element = elements[j]
			if (!element.state.PRE_PROCESS) this.PreProcess(element)
			element.next.px = Math.round(element.next.x)
			element.next.py = Math.round(element.next.y)
		}
	},
	
	PostProcess: function (element) {
		if (element.postprocess_func) element.postprocess_func()
		this.StepElement(element)
		element.state.PRE_PROCESS = false
		element.state.APPEARING = false
		element.state.POST_PROCESS = true
	},

	PostProcessQueue: function () {
		for (var j = 0 ; j < elements.length ; ++j) {
			var element = elements[j], state = element.state
			if (state.PRE_PROCESS) {
				if (state.COLLISION) {
					state.COLLISION = false
				} else {
					state.DEFY_PHYSICS = false
				}
			}
			if (state.DISAPPEARING) continue
			// Drawing commands queued here (line 882)
			this.PostProcess(element)
		}
		// Remove dead elements
		elements = elements.filter(function (e) { return !e.state.DISAPPEARING })
	},


	// Process collisions. In order to achieve sub-frame pixel-perfect precision efficiently
	// (and, I assume, in a thread-safe manner), the original function is a tangled mess. This
	// version is much less efficient, and may give slightly sub-optimal results when 3 objects
	// collide in the same frame, but I think it's still good.
	// If I get a chance I'll revisit this.
	ProcessCollisions: function () {
		for (var i = 0 ; i < elements.length ; ++i) {
			var element = elements[i]
			var state = element.state
			for (var j = i + 1 ; j < elements.length ; ++j) {
				var testelement = elements[j]
				var teststate = testelement.state
				// There was a test here to possibly call PreProcess on testelement before
				// proceeding - I'm assuming all elements have already been preprocessed.
				if (!collide.CollisionPossible(element, testelement)) continue
				if ((state.FINITE_LIFE || teststate.FINITE_LIFE) &&
					((state.APPEARING && element.life_span > 1) ||
					 (teststate.APPEARING && testelement.life_span > 1))) {
					continue
				}
				//console.log(element, testelement, element.vx, element.vy, testelement.vx, testelement.vy, element.next, testelement.next)
				var collided = collide.DrawablesIntersect(element, testelement)
				if (!collided) continue
			    //if (element.origin || testelement.origin) console.log("laser hit", collided)
				//console.log(collided, element, testelement)
				if (!collide.approaching(element, testelement)) continue
//				console.log(collided)
				//console.log("np", n1.x-n0.x, n1.y-n0.y, collided[3]-collided[1], collided[4]-collided[2])
				// TODO: this includes a temporary hack for lasers
				if ((!element.state.FINITE_LIFE || element.origin) &&
				    (!testelement.state.FINITE_LIFE || testelement.origin)) {
					var n0 = element.next, n1 = testelement.next
					n0.x = n0.px = collided[1]
					n0.y = n0.py = collided[2]
					n1.x = n1.px = collided[3]
					n1.y = n1.py = collided[4]
					if (!element.state.FINITE_LIFE && !testelement.state.FINITE_LIFE) {
						collide.collide(element, testelement)
					}
				}
				if (testelement.state.PLAYER_SHIP) {
					testelement.collision_func(element)
					element.collision_func(testelement)
				} else {
					testelement.collision_func(element)
					element.collision_func(testelement)
				}
			}
		}
	},
	
	Untarget: function (element) {
		// TODO: is elements a global here or what?
		elements.forEach(function (e) {
			if (e.target === element) e.target = null
		})
	},
	
}



